#ifndef Lexer_HEAD
#define Lexer_HEAD
#include "symbols.hpp"
#include <string>
#include <map>
using std::map;
using std::string;

class Tag{
public:
    const static int 
        AND = 256,  BASIC = 257,    BREAK = 258,DO = 258,   ELSE = 260,
        EQ = 261,   FALSE = 262,    GE = 263,   ID = 264,   IF = 265,
        INDEX = 266,LE = 267,       MINUS = 268,NE = 269,   NUM = 270,
        OR = 271,   REAL = 272,     TEMP = 273, TRUE = 274, WHILE = 275;
};

class Token{
public:
    Token(int t=0):m_tag(t){}
    int tag(){
        return m_tag;
    }
    int tag()const{
        return m_tag;
    }
    string toString(){
        return string("")+static_cast<char>(m_tag); //这里的m_tag是ASCII，所以用静态转化就行了。
    }
    bool operator==(Token &t){
        return m_tag==t.m_tag;
    }
private:
    int m_tag;
};

class Num:public Token
{
public:
    Num(int v=0):Token(Tag::NUM),m_value(v){}
    int value(){
        return m_value;
    }
    string toString(){
        return std::to_string(m_value);             //这里的 m_value是数字，不能用静态转化
    }
private:
    int m_value; 
};

/*
Word用于管理保留字、标识符和像&&这样的复合语法单元的词素。
*/
class Word:public Token{
public:
    Word(string s="",int tag = 0):Token(tag),m_lexme(s){}
    string lexme(){
        return m_lexme;
    }
    string lexme()const{
        return m_lexme;
    }
    string toString(){
        return m_lexme;
    }
    static const Word 
    And,Or,Eq,Ne,Le,Ge,Minus,True,False,Temp;
private:
    string m_lexme;         //语义
};
const Word Word::And("&&",Tag::AND);
const Word Word::Or("||",Tag::OR);
const Word Word::Eq("==",Tag::EQ);
const Word Word::Ne("!=",Tag::NE);
const Word Word::Le("<=",Tag::LE);
const Word Word::Ge(">=",Tag::GE);
const Word Word::Minus("minus",Tag::MINUS);     // 用于抽象语法树
const Word Word::True("true",Tag::TRUE);        
const Word Word::False("false",Tag::FALSE);     
const Word Word::Temp("temp",Tag::TEMP);        // 用于抽象语法树

class Real:public Token{//处理浮点数
public:
    Real(float v=0):Token(Tag::REAL),m_value(v){}
    string toString(){
        return std::to_string(m_value);
    }
    float value(){
        return m_value;
    }
private:
    float m_value;
};

class Lexer{
public:
    inline void reserve(Word t){
        words[t.lexme()] = t;
    }
    inline void reserve(const Type& t){
        Word temp = const_cast<Type&>(t);
        words[temp.lexme()] = temp;
    }
    static int line;
    Lexer():peek(' '){
        reserve(Word("if",Tag::IF));
        reserve(Word("else",Tag::ELSE));
        reserve(Word("while",Tag::WHILE));
        reserve(Word("do",Tag::DO));
        reserve(Word("break",Tag::BREAK));
        reserve(Word::True);
        reserve(Word::False);
        //还有四个类型Type
        reserve(Type::Int);
        reserve(Type::Float);
        reserve(Type::Char);
        reserve(Type::Bool);
        
    }
    Token scan(){
        std::cout<<"in scan"<<std::endl;
        for(;;readch()){
            if(peek == ' '||peek == '\t')continue;
            else if(peek == '\n')line++;
            else break;
        }
        switch (peek)
        {
        case '&':{
            if(readch('&'))return Word::And;
            else return Token('&');
        }
        case '|':{
            if(readch('|'))return Word::Or;
            else return Token('|');
        }
        case '=':{
            if(readch('='))return Word::Eq;
            else return Token('=');
        }
        case '!':{
            if(readch('='))return Word::Ne;
            else return Token('!');
        }
        case '<':{
            if(readch('='))return Word::Le;
            else return Token('<');
        }
        case '>':{
            if(readch('='))return Word::Ge;
            else return Token('>');
        }
        default:
            break;
        }
        if(isDigit(peek)){
            int v = 0;
            do{
                v = v<<2+v<<1+peek - '0';
                peek = getchar();
            }while(isDigit(peek));
            if(peek!='.')return Num(v);     //整数部分
            float x = v;
            float d = 10;
            for(;;){
                readch();
                if(isDigit(peek)){
                    x = x + (peek-'0')/d;
                    d*=10;
                }
            }
            return Real(x);                 //小数部分
        }
        else if(isLetter(peek)){
            string b;
            do{
                b = b + peek;
                peek = getchar();
            }while(isDigit(peek)||isLetter(peek));
            if(!words.count(b))words[b]=Word(b,Tag::ID);
            return words[b];
        }
        else if(peek == '/'){
            comment();
            return scan();
        }
        Token t(peek);
        peek = ' ';
        return t;
    }
    void showLexer(){
        std::cout<<"--------------------Lexer Words--------------------"<<std::endl;
        for(auto& i:words){
            std::cout<<i.first<<" : "<<i.second.tag()<<", ";
        }
        std::cout<<std::endl;
        std::cout<<"--------------------Lexer Lines--------------------"<<std::endl;
        std::cout<<"lines : "<<line<<std::endl;
    }
private:
    inline void readch(){peek = getchar();}
    inline bool readch(char c){
        readch();
        return peek==c?peek=' ',true:false;
    }
    inline bool isDigit(const char& c){
        return c>='0'&&c<='9';
    }
    inline bool isLetter(const char&c){
        return (c>='a'&&c<='z')||(c>='A'&&c<='Z');
    }
    void comment(){
        peek = getchar();
        if(peek == '/')line_comment();
        else if(peek == '*')block_comment();
        else{
            std::cout<<"peek:"<<peek<<std::endl;
            throw("Comment Syntax Error!");
        }
    }
    void line_comment(){    
        while((peek = getchar())!='\n'&&peek!=EOF);
        if(peek==EOF)throw("Line Comment Syntax Error!");
        line++;
    }
    void block_comment(){
        do{
            peek = getchar();
            if(peek == '\n')line++;
        }while(peek!='*'&&peek != EOF);
        if(peek == '*')block_commen_star();
        else throw("Block Comment Synatax Error!");
    }
    void block_commen_star(){//input a star when scan block comment
        peek = getchar();
        if(peek == '/'){
            peek = ' ';
            return;
        }
        else block_comment();
    }

    char peek;
    map<string,Word>words;
};
int Lexer::line = 1;
#endif